# -----------------------------------------------------------------------------
# Copyright (c) 2025, Southeast University (China)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#  - Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  - Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#  - Neither the name of the copyright holders nor the names of its
#    contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Authors: Jiajie Xu, Yanfang Zhang, Jiaqi Gao, Leyun Tian
# -----------------------------------------------------------------------------


class Parasitics(object):
    def _generate_names(self):
        for endpoint, _ in self.endpoints_name:
            yield endpoint
        for cap in self.caps_name:
            if len(cap) == 2:
                yield cap[0]
            else:
                yield cap[0]
                yield cap[1]
        for res in self.ress_name:
            yield res[0]
            yield res[1]
        
    def __init__(self, endpoints, caps, ress):# endpoints:[(pin, IO)] caps [(node1, cap) or (node1, node2, cap)] res [(node1, node2, res)]
        self.endpoints_name = endpoints
        self.caps_name = caps
        self.ress_name = ress

        self.name2id = {} # n3:1 -> 1
        self.names = []
        for name in self._generate_names():
            if name not in self.name2id:
                self.name2id[name] = len(self.name2id)
                self.names.append(name)

        self.endpoints = [
            (self.name2id[name], io) #(node, I/O)
            for name, io in endpoints
        ]
        self.grounded_caps = [ #(node, cap value)
            (self.name2id[cap[0]], cap[1])
            for cap in caps if len(cap) == 2
        ]
        self.coupling_caps = [ #(node1, node2, cap)
            (self.name2id[cap[0]], self.name2id[cap[1]], cap[2])
            for cap in caps if len(cap) == 3
        ]
        self.ress = [ #(node1, node2, res)
            (self.name2id[res[0]], self.name2id[res[1]], res[2])
            for res in ress
        ]
        # len of nodes
        self.n = len(self.name2id)

        self.sink_cell_caps = []

    def _total_cap(self):
        total_cap = 0
        for _, cap in self.grounded_caps:
            total_cap += cap
        for sink_caps in self.sink_cell_caps:
            total_cap += sink_caps
        return total_cap
            
    def __repr__(self):
        lines = []
        lines.append(f"<Parasitics object at {hex(id(self))}>")
        lines.append(f"  Total nodes: {self.n}")
        lines.append(f"  Node names (name2id):")
        for name, idx in self.name2id.items():
            lines.append(f"    {name}: {idx}")

        lines.append(f"  Endpoints:")
        for nid, io in self.endpoints:
            lines.append(f"    {self.names[nid]} ({io})")

        lines.append(f"  Grounded Caps:")
        for nid, cap in self.grounded_caps:
            lines.append(f"    {self.names[nid]}: {cap}")

        lines.append(f"  Coupling Caps:")
        for nid1, nid2, cap in self.coupling_caps:
            lines.append(f"    {self.names[nid1]} - {self.names[nid2]}: {cap}")

        lines.append(f"  Resistors:")
        for nid1, nid2, res in self.ress:
            lines.append(f"    {self.names[nid1]} - {self.names[nid2]}: {res}")

        if self.sink_cell_caps:
            lines.append(f"  Sink Cell Caps:")
            for item in self.sink_cell_caps:
                lines.append(f"    {item}")

        return "\n".join(lines)

def Read_Spef(inspef):
    """Read spef file, return list of nets with RC value and connection relationship
        return SpefNets {netname: Parasitics}"""
    # set default unit and unit scaler
    scaler = {'*T_UNIT': 1, '*C_UNIT': 1, '*R_UNIT': 1, '*L_UNIT': 1}
    unit_map = {'N': 1e-9, 'P': 1e-12, 'M': 1e-3, 'U': 1e-6, 'K': 1e3}
    default_unit = {'*T_UNIT': 'N', '*C_UNIT': 'P', '*R_UNIT': 'K', '*L_UNIT': ''}

    namemap = {} # *number -> real name
    port_location = {} # portname -> port location
    flag_namemap, port_flag, connect_flag, cap_flag, res_flag = False, False, False, False, False
    SpefNets = {}
    with open(inspef, 'r') as infile:
        for line in infile:
            # IO flag 0--->O , 1---> I , 2---> interal
            endpoint_flag = None
            index = line.split()
            if len(index) > 0:
                """Unit part process"""
                if len(index) == 3 and index[0] in scaler:
                    prefix = index[2][0].upper()
                    scaler[index[0]] = unit_map.get(prefix, 1)/unit_map.get(default_unit[index[0]], 1)
                
                if index[0] == "*D_NET":
                    port_flag = False
                    try:
                        name
                    except NameError:
                        pass
                    else:
                        SpefNets[name] = Parasitics(endpoints=endpoints, caps=caps, ress=ress)
                        SpefNets[name].sink_cell_caps = sink_cell_caps
                    endpoints = []
                    caps = []
                    ress = []
                    sink_cell_caps = []
                    name = namemap[index[1]]
                if index[0] == "*END":
                    res_flag = False
                """Resistance part process"""
                if res_flag:
                    c = index[1:]
                    for i, item in enumerate(c):
                        if i != len(c) - 1:
                            if ":" in c[i]:
                                c[i] = namemap[c[i].split(":")[0]] + "/" + c[i].split(":")[1]
                            else:
                                c[i] = namemap[c[i]]
                    c[-1] = float(c[-1])  * scaler['*R_UNIT']
                    ress.append(tuple(c))
                if index[0] == "*RES":
                    res_flag = True
                    cap_flag = False

                """Capacitance part process"""
                if cap_flag:
                    c = index[1:]
                    for i, item in enumerate(c):
                        if i != len(c) - 1:
                            if ":" in c[i]:
                                c[i] = namemap[c[i].split(":")[0]] + "/" + c[i].split(":")[1]
                            else:
                                c[i] = namemap[c[i]]
                    c[-1] = float(c[-1])  * scaler['*C_UNIT']
                    caps.append(tuple(c))
                if index[0] == "*CAP":
                    cap_flag = True
                    connect_flag = False
                
                """Connection part process"""
                if connect_flag:
                    if index[0] == "*P":
                        # the Port with I is equal cells pins with O
                        if index[2] == "I":
                            io = "O"
                        else:
                            io = "I"
                            for i in range(len(index)):
                                if index[i] == "*L":
                                    sink_cell_caps.append(float(index[i+1]) * scaler['*C_UNIT'])
                    else:
                        if index[2] == "I":
                            io = "I"
                            for i in range(len(index)):
                                if index[i] == "*L":
                                    sink_cell_caps.append(float(index[i+1]) * scaler['*C_UNIT'])
                        else:
                            io = "O"
                    if ":" in index[1]:
                        pin = namemap[index[1].split(":")[0]] + "/" + index[1].split(":")[1]
                    else:
                        pin = namemap[index[1]]
                    if io == "O":
                        # the driver pin should be the first pin
                        endpoints.insert(0, (pin, io))
                    else:
                        endpoints.append((pin, io))
                if index[0] == "*CONN":
                    connect_flag = True
                
                """Port part process"""
                if port_flag:
                    port_location[namemap[index[0]]] = (float(index[3]), float(index[4]))
                if index[0] == "*PORTS":
                    flag_namemap = False
                    port_flag = True
                
                """Namemap part process"""
                if flag_namemap:
                    namemap[index[0]] = index[1]
                if index[0] == "*NAME_MAP":
                    flag_namemap = True

        SpefNets[name] = Parasitics(endpoints=endpoints, caps=caps, ress=ress)
        SpefNets[name].sink_cell_caps = sink_cell_caps
        

    return SpefNets

if __name__ == "__main__":
    import sys 
    import os
    sys.path.append("../")
    from Global_var import *
    import work_var
    file = os.path.join(work_var.inn_data_dir, "s27/s27_postRoute_fill_std_rcworst_CCworst.spef")
    SpefNets = Read_Spef(file)
    
    ###
    #print(f'SpefNets_len = {len(SpefNets)}')
    for spef in SpefNets.values():
        print(spef)
        print(spef._total_cap())
    
    #SpefNets[]
    # for value in SpefNets.values():
    #     value.print_rc_network()